﻿using Cysharp.Threading.Tasks;
using Wordgames;
using System.Data;
using WordGames.System;
using WordGames;
using Net.Helper;

namespace DistributedExample
{
    public class DBManager
    {
        public async void Init()
        {
            int affectedCount = 0;
            string nodeName = "";
            int state = 0;
            long dataTotal = 0;
            foreach (var node in ClusterSystem.DBCluster.GetDBNodes())
            {
                var dataCount = node.ExecuteScalar<long>($"SELECT COUNT(*) FROM `user`;");
                dataTotal += dataCount;
                Console.WriteLine($"节点:{node.Config.Database} 数据量:{dataCount}");
            }
            Console.WriteLine($"总数据量:{dataTotal}");
            Console.WriteLine("1.输入:a dbName 01新增1个数据库节点,机器号为01");
            Console.WriteLine("2.输入:add 10000新增1万个数据");
            Console.WriteLine("3.输入:r dbName移除1个数据库节点");
            while (true)
            {
                var command = Console.ReadLine();
                if (command.StartsWith("add"))
                {
                    var countText = command.Remove(0, 4);
                    var count = int.Parse(countText);
                    var random = new Random(977579129);
                    for (int i = 0; i < count; i++)
                    {
                        var data = new UserData
                        {
                            Account = Guid.NewGuid().ToString() + random.Next(int.MinValue, int.MaxValue).ToString()
                        };
                        data.Context = ClusterSystem.DBCluster.GetDBNode(data.Account);
                        data.NewTableRow();
                    }
                    affectedCount = 0;
                    await Task.Delay(1000);
                    dataTotal = 0;
                    foreach (var node in ClusterSystem.DBCluster.GetDBNodes())
                    {
                        node.WaitBatchWorker(); //把上面的添加全部执行完成后才能往下执行
                        var dataCount = node.ExecuteScalar<long>($"SELECT COUNT(*) FROM `user`;");
                        dataTotal += dataCount;
                        Console.WriteLine($"节点:{node.Config.Database} 数据量:{dataCount}");
                    }
                    Console.WriteLine($"总数据量:{dataTotal}");
                    continue;
                }
                if (command.StartsWith("r"))
                {
                    nodeName = command.Remove(0, 2);
                    if (ClusterSystem.DBCluster.GetNodeName(nodeName) == null)
                    {
                        Console.WriteLine("要移除的节点不存在!");
                        continue;
                    }
                    state = 2;
                }
                else if (command.StartsWith("a"))
                {
                    nodeName = command.Split(" ")[1];
                    if (ClusterSystem.DBCluster.GetNodeName(nodeName) != null)
                    {
                        Console.WriteLine("新增的节点已存在!");
                        continue;
                    }
                    state = 1;
                }
                else continue;
                // 获取受影响的节点列表
                var affectedNodes = ClusterSystem.DBCluster.Hashing.GetAffectedNodes(nodeName);
                WordgamesDB removeDB = null;
                if (state == 1)
                {
                    var agrs = command.Split(" ");
                    var dbConfig = new DBConfig()
                    {
                        DBName = agrs[1],
                        User = "root",
                        Password = "root",
                        MachineId = int.Parse(agrs[2]),
                        Host = "127.0.0.1:3306",
                    };
                    ClusterSystem.DBCluster.AddDBNode(dbConfig);
                    ClusterSystem.Config.DBConfig.Add(dbConfig);
                    PersistHelper.Serialize(ClusterSystem.Config, "config.ini");
                }
                else
                {
                    affectedNodes = ClusterSystem.DBCluster.Hashing.RemoveNodeGet(nodeName, out removeDB);
                }
                // 输出受影响的节点
                Console.WriteLine("受影响的节点：");
                foreach (var node in affectedNodes)
                {
                    Console.WriteLine($"虚拟节点:{node.VirtualNodeName} 物理节点:{node.PhysicalNodeName}");
                }
                var physicalNodeNames = affectedNodes.Select(item => item.Token).ToHashSet();
                foreach (var gameDB in physicalNodeNames)
                {
                    //找出这些节点所在的数据库
                    var userIdMax = gameDB.ExecuteScalar<long>(@"SELECT MAX(id) FROM `user`;");
                    long currRowsCount = 0;
                    do
                    {
                        long size = 1000000; //正常情况下100万数据加载是没有问题的
                        if (currRowsCount + size >= userIdMax)
                            size = userIdMax - currRowsCount;
                        var users = gameDB.ExecuteQueryList<UserData>($"SELECT * FROM `user` WHERE id >= {currRowsCount} AND id <= {currRowsCount + size};");
                        currRowsCount += size;
                        for (int i = users.Length - 1; i >= 0; i--)
                        {
                            var data = users[i];
                            var virtualNode = ClusterSystem.DBCluster.Hashing.GetNode(data.Account); //重新计算要迁移到哪个节点
                            if (virtualNode.Token == gameDB) //重新计算得出, 这个数据不需要迁移
                                continue;
                            data.Delete(); //从旧节点移除这个数据
                            var newData = new UserData()
                            {
                                Account = data.Account,
                                Password = data.Password,
                                Name = data.Name,
                                Level = data.Level,
                            };
                            newData.Context = ClusterSystem.DBCluster.GetNodeName(virtualNode.PhysicalNodeName); //迁移数据到新的节点去
                            newData.NewTableRow();
                            affectedCount++;
                        }
                    }
                    while (currRowsCount < userIdMax);
                }
                if (state == 2)
                {
                    removeDB.Stop();
                    removeDB.WaitBatchWorker();
                    removeDB.ExecuteNonQuery($"DROP DATABASE {nodeName};");
                    ClusterSystem.Config.DBConfig.RemoveAll(dbConfig => dbConfig.DBName == nodeName);
                    PersistHelper.Serialize(ClusterSystem.Config, "config.ini");
                }
                Console.WriteLine("迁移数据量:" + affectedCount);
                affectedCount = 0;
                await Task.Delay(1000);
                long totalDataCount = 0L;
                foreach (var gameDB in ClusterSystem.DBCluster.GetDBNodes())
                {
                    //检查数据是否冗余
                    gameDB.WaitBatchWorker(); //执行所有批处理才能执行下面代码
                    var userIdMax = gameDB.ExecuteScalar<long>(@"SELECT MAX(id) FROM `user`;");
                    long currRowsCount = 0;
                    long dataCount = 0;
                    do
                    {
                        long size = 1000000; //正常情况下100万数据加载是没有问题的
                        if (currRowsCount + size >= userIdMax)
                            size = userIdMax - currRowsCount;
                        var users = gameDB.ExecuteQueryList<UserData>($"SELECT * FROM `user` WHERE id >= {currRowsCount} AND id <= {currRowsCount + size};");
                        currRowsCount += size;
                        dataCount += users.LongLength;
                        for (int i = users.Length - 1; i >= 0; i--)
                        {
                            var data = users[i];
                            var virtualNode = ClusterSystem.DBCluster.Hashing.GetNode(data.Account); //重新计算要迁移到哪个节点
                            if (virtualNode.Token == gameDB) //重新计算得出, 这个数据不需要迁移
                                continue;
                            affectedCount++;
                        }
                    }
                    while (currRowsCount < userIdMax);
                    Console.WriteLine($"节点:{gameDB.Config.Database} 数据量:{dataCount}");
                    totalDataCount += dataCount;
                }
                Console.WriteLine("冗余数据量:" + affectedCount);
                Console.WriteLine("总数据量:" + totalDataCount);
            }
        }
    }
}
